package jmine.tec.persist.schema.impl.parsers;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;

import jmine.tec.persist.schema.impl.elements.Schema;
import jmine.tec.persist.schema.impl.elements.TableElement;

/**
 * Parser de statements de create table.
 * 
 * @author lundberg
 */
public class TableParser extends AbstractSchemaElementParser<TableElement> {

    private static final int TABLE_NAME_GROUP_INDEX = 1;

    private static final int COLUMNS_GROUP_INDEX = 2;

    private PrimaryKeyTableParser pkParser;

    private AlternateKeyTableParser akParser;

    private ColumnParser columnParser;

    public static final String TABLE_NAME_REGEX = "[\\w.]+";

    private final Schema schema;

    /**
     * Construtor
     * 
     * @param schema schema
     */
    public TableParser(Schema schema) {
        super("(?s)create\\s+table\\s+(" + TABLE_NAME_REGEX + ")\\s+\\((.*)\\)", schema);
        this.pkParser = new PrimaryKeyTableParser(schema);
        this.akParser = new AlternateKeyTableParser(schema);
        this.columnParser = new ColumnParser(schema);
        this.schema = schema;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public TableElement doParse(Matcher matcher) {
        TableElement table = new TableElement(this.schema, matcher.group(TABLE_NAME_GROUP_INDEX));
        this.parseColumns(table, matcher.group(COLUMNS_GROUP_INDEX));
        return table;
    }

    /**
     * Cria um mapa de nome da coluna para seu tipo, dado um statement de create table.
     * 
     * @param table table
     * @param st st
     */
    private void parseColumns(TableElement table, String st) {
        List<String> l = new ArrayList<String>();
        StringBuilder builder = new StringBuilder();
        int paren = 1;
        for (char c : st.toCharArray()) {
            if (c == '(') {
                builder.append(c);
                ++paren;
            } else if (c == ')') {
                --paren;
                if (paren != 0) {
                    builder.append(c);
                } else {
                    this.addToList(l, builder);
                    l.add(builder.toString().replaceAll("\\s+", " ").trim());
                }
            } else if (c == ',') {
                if (paren == 1) {
                    l.add(builder.toString().replaceAll("\\s+", " ").trim());
                    builder = new StringBuilder();
                } else {
                    builder.append(c);
                }
            } else {
                builder.append(c);
            }
        }
        l.add(builder.toString().replaceAll("\\s+", " ").trim());
        for (String s : l) {
            if (this.pkParser.matches(s)) {
                table.setPrimaryKey(this.pkParser.parse(s));
            } else if (this.akParser.matches(s)) {
                table.addAlternateKey(this.akParser.parse(s));
            } else if (this.columnParser.matches(s)) {
                table.addColumn(this.columnParser.parse(s));
            } else if (s.length() != 0) {
                throw new IllegalStateException("Parâmetro de tabela não reconhecido: " + s);
            }
        }
    }

    /**
     * Adiciona o sub-statement contido no builder à lista e trunca o builder.
     * 
     * @param list list
     * @param builder builder
     */
    private void addToList(List<String> list, StringBuilder builder) {
        String result = builder.toString().replaceAll("\\s+", " ").trim();
        if (result.length() != 0) {
            list.add(result);
        }
        builder.setLength(0);
    }
}
